package top.braycep.file;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import top.braycep.bean.JarFile;
import top.braycep.bean.JarInnerFile;
import top.braycep.exception.BaseException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

public class JarUtils {
    private static final Log logger = LogFactory.get();

    public static void createJarFile(JarFile file, boolean overwrite) throws IOException {
        String filepath = file.getFilepath();
        if (StrUtil.isEmpty(file.getFilename()) || StrUtil.isEmpty(filepath)) {
            throw new BaseException("\u6587\u4EF6\u540D\u6216\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
        }
        if (!new File(filepath).exists()) {
            throw new BaseException("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u5B58\u5728");
        }

        String filename = file.getFilename();
        if (!filename.endsWith(".jar")) {
            filename = filename.concat(".jar");
        }
        logger.info(">>> begin to parse file: {}", filename);
        File jarFile = new File(filepath, filename);
        if (jarFile.exists()) {
            if (overwrite) {
                logger.info(">>> file: {} is deleted", jarFile.getAbsolutePath());
                jarFile.delete();
            } else {
                throw new BaseException("文件[" + jarFile.getAbsolutePath() + "]已存在");
            }
        }
        jarFile.createNewFile();

        JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile));

        List<JarInnerFile> innerFiles = file.getInnerFiles();
        for (JarInnerFile innerFile : innerFiles) {
            String realpath = innerFile.getRealpath();

            if (filepath.startsWith(realpath)) {
                logger.warn(">>> wrong inner file path: {} for it include jar path: {}", realpath, filepath);
                continue;
            }

            String mappedPath = innerFile.getMappedpath();
            String rule = innerFile.getRule();

            mappedPath = mappedPath.replaceAll("[.\\\\]+", "/");

            innerCreate(jos, mappedPath, new File(realpath), rule, true);
        }

        logger.info(">>> jar file: {} parse end", filename);

        jos.close();
    }

    private static void innerCreate(JarOutputStream jos, String mappath, File innerFile, String rule, boolean isFirt) throws IOException {
        if (innerFile.exists()) {
            if (!mappath.endsWith("/")) {
                mappath = mappath.concat("/");
            }
            if (innerFile.isDirectory()) {
                if (!isFirt) {
                    mappath = mappath.concat(innerFile.getName());
                }
                for (File file : Objects.requireNonNull(innerFile.listFiles())) {
                    innerCreate(jos, mappath, file, rule, false);
                }
            } else {
                String entryName = mappath.concat(innerFile.getName());
                entryName = StrUtil.removePrefix(entryName, "/");
                List<String> rules = StrUtil.split(rule, ';', true, true);
                String r = rules.stream().filter(entryName::matches).findAny().orElse(null);
                if (r != null) {
                    logger.info(">>> ---file: {} for rule: {}", entryName, r);
                    return;
                }
                logger.info(">>> +++file: {} size: {}kb", entryName, innerFile.length() / 1024);
                JarEntry entry = new JarEntry(entryName);
                jos.putNextEntry(entry);
                FileInputStream fis = new FileInputStream(innerFile);
                int len;
                byte[] bytes = new byte[8192];
                while ((len = fis.read(bytes)) != -1) {
                    jos.write(bytes, 0, len);
                }
                fis.close();
            }
        }
    }
}
